<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link rel="shortcut icon" type="image/png" href="icon.png">

<title>GitHub页面历史版本访问-简易版工具</title>

</head>

<body>
<div style="font-size:16px;line-height:1.4">
<div style="padding:10px;padding-bottom:5px;background:#fff;border-bottom:4px solid #f60;border-radius: 8px;position:relative;">
	<div style="display:inline-block;vertical-align:middle;margin-right:50px;line-height:1">
		<div style="font-size:24px;color:#f60">GitHub页面历史版本访问-简易版工具</div>
		
		<div style="font-size:12px;margin-top:5px">
			<span title="是有多无聊才会写出这个页面，想学吗？Ctrl+U查看源码！"
				style="cursor:zoom-in;color:#f60;margin-right:115px">
				<script>
				/*是有多无聊才会写出这个页面，真查看源码，是想学吗？*/
				var s=unescape('%uD83D%uDC97');document.write(s+s+"后会有期 at 2022-03-06"+s+s);
				</script>
			</span>
			
			<a href="https://github.com/xiangyuecn/Recorder" target="_blank">GitHub</a>
			| <a href="https://gitee.com/xiangyuecn/Recorder" target="_blank">Gitee</a>
		</div>
	</div>
	
	<span style="margin-right:20px;display:inline-block">
		仓库路径：
		<input class="OldVer_Url" placeholder="格式：`user:repo@*,path` @可选:@提交hash @分支名" style="height:24px;width:340px;width:min(340px, 70vw)">
	</span>
	<span style="margin-right:20px;display:inline-block">
		版本：
		<select class="OldVer_Ver" style="height:30px;width:200px"></select>
	</span>
	<span>
		<button style="padding:5px 30px" onclick="OldVerReload()">加载</button>
	</span>
</div>
<div class="OldVer_LogsElem" style="padding:15px 20px">
	<div class="OldVer_Logs" style="border:1px solid #f5f5f5;background:#fff;padding:12px;border-radius: 6px;box-shadow: 2px 2px 3px #aaa;"></div>
</div>
</div>

<script>
(function(){

window.OldVerReload=function(){
	var url=document.querySelector(".OldVer_Url").value;
	var ver=document.querySelector(".OldVer_Ver").value;
	var info=parseUrl(url);
	if(url && !info){
		alert("仓库路径无效");
		return;
	}
	if(info && info.inVer){
		if(info.inVer!=urlInfo.inVer){//已修改路径中的ver
			ver="";
		}else if(ver!=inVerTxt){//下拉选择了版本号
			info.inVer="";
			url=toUrl(info);
		}
	}
	if(ver==inVerTxt){
		ver="";
	}
	location.href="#ver="+encodeURIComponent(ver)+"&url="+encodeURIComponent(url);
	location.reload();
};

//xss!
var args_url=decodeURIComponent((/\burl=([^&#]+)/.exec(location.href)||[])[1]||"xiangyuecn:Recorder,/");
var args_ver=decodeURIComponent((/\bver=([^&#]+)/.exec(location.href)||[])[1]||"");
document.querySelector(".OldVer_Url").value=args_url;

var toUrl=function(o){
	return o.owner+":"+o.repo+(o.inVer?"@"+o.inVer:"")+","+o.path;
};
var parseUrl=function(url){
	var m=/([^:]+):([^,@]+)(?:@([^,]+))?,(.+)/i.exec(url);
	if(!m)return null;
	return {
		owner:m[1]
		,repo:m[2]
		,inVer:m[3]||""
		,path:m[4]
		,url:"https://"+m[1]+".github.io/"+m[2]+m[4]
	};
};
var urlInfo=parseUrl(args_url)||{};
if(urlInfo.inVer){
	args_ver=urlInfo.inVer;
}
var inVerTxt="_InVer_";
var rootUrl="";

console.log("urlInfo",urlInfo);
reclog('<span style="font-weight:bold">本工具支持简单处理 GitHub Pages 的历史版本页面的访问展示</span>'
	+'；<span style="color:#f60">如果JsDelivr CDN无法访问，本工具将无法运行</span>'
	+'；【用法】：仓库路径中填写 <span style="color:#cb05ff">GitHub 用户名 + : + 仓库名 + 可选@（@Release版本号、@提交Hash、@分支名称） + , + /文件路径</span> （仓库路径中填写的@值比选择的版本号优先级高），版本可以先不选，然后点击加载。首次加载好后，可以切换版本号（或明确在仓库路径中指定分支、某次提交）来查看不同历史的页面效果。');
reclog("当前处理的地址为："+ToA(urlInfo.url)+" 指定的版本号："+(args_ver||"未指定"));


//简单拦截处理地址
var oldAppend=HTMLElement.prototype.appendChild;
HTMLElement.prototype.appendChild=function(elem){
	if(elem.nodeName=="SCRIPT" && elem.getAttribute("src")){
		elem.setAttribute("src",replaceSrc("node.script",elem.getAttribute("src")));
	}else if(elem.nodeName=="LINK" && elem.getAttribute("href")){
		elem.setAttribute("href",replaceSrc("node.link",elem.getAttribute("href")));
	}
	return oldAppend.apply(this,arguments);
};
var oldOpen=XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open=function(m,u){
	var a=[];a.push.apply(a,arguments);
	a[1]=replaceSrc("xhr."+m,u);
	return oldOpen.apply(this,a);
};
XMLHttpRequest.prototype.setRequestHeader=function(k,v){
	console.warn("xhr阻止添加请求头:"+k+"="+v);//沙雕jQuery会添加X-Requested-With
};
var oldWrite=document.write;
document.write=function(){
	var a=[],v=arguments;
	for(var i=0;i<v.length;i++)a[i]=replaceHtmlAllSrc("document.write",v[i]);
	return oldWrite.apply(this,a);
};
function replaceSrc(tag,src){
	if(!urlInfo._dir){
		urlInfo._dir=urlInfo.path.replace(/\/[^\/]+$/g,"/");
		urlInfo._locDir=location.href.replace(/[\?#].*/g,"").replace(/\/[^\/]+$/g,"/");
		urlInfo._locRoot=urlInfo._locDir.replace(/\/assets\//ig,"/");
		console.log("urlInfo",urlInfo);
	};
	var dir=urlInfo._dir;
	var val,m;
	if(/^([\w:-]+)?\/\//.test(src)){//完整地址
		val=src;
		//当前url下的地址
		if(src.toLowerCase().indexOf(urlInfo._locRoot.toLowerCase())==0){
			val=rootUrl+"/"+src.substr(urlInfo._locRoot.length);
		};
	}else if(/^[\w-]+:/.test(src)){//dataurl
		val=src;
	}else if(/^\//.test(src)){//绝对地址
		val=rootUrl+src;
	}else if(/^\.+/.test(src)){//上级目录
		var exp=/^\.+\/?(.*)/;
		while(m=exp.exec(src)){
			src=m[1];
			dir=dir.replace(/\/[^\/]+\/$/,"/");
		};
		val=rootUrl+dir+src;
	}else{//当前目录
		val=rootUrl+dir+src;
	};
	console.log((val==src?"未":"")+"替换"+tag,src+(val==src?"":" -> "+val));
	return val;
};
function replaceHtmlAllSrc(from,html){
	html=replaceHtml_node(from,html,"link","href");
	html=replaceHtml_node(from,html,"script","src");
	return html;
};
function replaceHtml_node(from,html,tag,key){
	var exp=new RegExp("(<"+tag+"[^>]+"+key+"\\s*=\\s*[\"'])([^>]+?)([\"'][^>]*>)","ig");
	html=html.replace(exp,function(a,b,c,d){
		return b+replaceSrc(from+"@"+tag,c)+d;
	});
	return html;
};



function Step_LoadVers(){
	if(!urlInfo.repo || !urlInfo.owner){
		reclog("地址无效",1);
		return;
	}
	var url="https://cdn.jsdelivr.net/gh/"+urlInfo.owner+"/"+urlInfo.repo+"/";
	reclog('正在拉取版本号列表... [sync]'+ToA(url),"#bbb");
	var loadEnd=function(txt){
		txt=txt.replace(/\r|\n/g,"");
		var vers=[],opts=[];
		var m=/<select[^>]*versions\b.+?<\/select/.exec(txt);
		if(txt=="-1"){
			//NOOP
		}else if(!m){
			reclog("版本号页面已拉取，但未匹配到版本号列表，可能是此仓库没有任何Releases",3);
		}else{
			txt=m[0];
			var exp=/<option.+?@([^"]+)"/g;
			while(m=exp.exec(txt)){
				vers.push(m[1]);
				opts.push('<option value="'+m[1]+'">'+m[1]+'</option>');
			};
			if(!vers.length){
				reclog("版本号页面已拉取，但未发现任何版本号，可能是此仓库没有任何Releases",3);
			}else{
				console.log("版本号列表",vers);
				reclog("版本号列表加载完成，发现"+vers.length+"个版本");
			}
		};
		
		args_ver=args_ver||vers[0];
		if(!args_ver){
			reclog("未提供版本号参数，已终止处理。",3);
			return;
		}
		rootUrl="https://cdn.jsdelivr.net/gh/"+urlInfo.owner+"/"+urlInfo.repo+"@"+args_ver;
		
		var slcVer=args_ver;//下拉选中的值
		if(vers.indexOf(args_ver)!=-1){
			//值在列表中 NOOP
		}else if(urlInfo.inVer){//路径中提供的
			slcVer=inVerTxt;
		}else{//不在列表中
			opts.splice(0,0,'<option value="'+args_ver+'">[未知版本]'+args_ver+'</option>');
		}
		
		opts.splice(0,0,'<option value="'+inVerTxt+'">路径中填写@分支名、提交Hash</option>');
		opts.splice(0,0,'<option value="">==不选==</option>');
		var elem=document.querySelector(".OldVer_Ver");
		elem.innerHTML=opts.join(" ");
		elem.value=slcVer;
		
		var id="a"+Math.random().toString(16).substr(2);
		reclog('要不要查看 ver:'+args_ver
			+' 的<input class="in_'+id+'" value="/README.md" style="width:300px;height:20px">文件内容？'
			+'<button onclick="OldVerLoadReadme(\''+id+'\')">拉取此文件</button>'
			+'<div class="div_'+id+'"></div>');
		
		Step_LoadPage();
	};
	Load(url,loadEnd,function(err){
		reclog("拉取列表失败："+err,"red;font-size:30px");
		loadEnd("-1");
	},true);
};

function Step_LoadPage(){
	var url=rootUrl+urlInfo.path;
	if(/\/$/.test(url)){
		url+="index.html";
	};
	reclog("正在拉取 ver:"+args_ver+" 页面... [sync]"+ToA(url),"#bbb");
	Load(url,function(html){
		var title=(/>([^<>]+)<\/title>/.exec(html)||[])[1]||"";
		if(title)document.title="[版本:"+args_ver+"] "+title;
		reclog("拉取成功【"+title.replace(/<|'|"/g,"")+"】，已开始执行原始页面内容↓↓↓↓↓↓",2);
		
		html=html.replace(/<!DOCTYPE.*?>/ig,"");
		html=replaceHtmlAllSrc("LoadPage",html);
		document.write(html);
	},function(err){
		reclog("拉取页面失败："+err,"red;font-size:30px");
	},true);
};


window.OldVerLoadReadme=function(id){
	var div=document.querySelector(".div_"+id);
	var path=document.querySelector(".in_"+id).value;
	if(!/^\//.test(path))path="/"+path;
	var url=rootUrl+path;
	div.innerHTML="正在拉取 ver:"+args_ver+" "+path+" ... [async]"+ToA(url);
	Load(url,function(txt){
		div.innerHTML="已拉取 ver:"+args_ver+' 的 '+path+' ： <div><textarea class="txt_'+id+'" style="width:90%;height:90vh"></textarea></div>';
		document.querySelector(".txt_"+id).value=txt;
	},function(err){
		div.innerHTML+='<div style="font-size:24px;color:red">拉取 '+path+" 失败："+err+"</div>";
	});
};







function reclog(s,color){
	var now=new Date();
	var t=("0"+now.getHours()).substr(-2)
		+":"+("0"+now.getMinutes()).substr(-2)
		+":"+("0"+now.getSeconds()).substr(-2);
	var div=document.createElement("div");
	var elem=document.querySelector(".OldVer_Logs");
	elem.appendChild(div);
	div.innerHTML='<div style="color:'+(!color?"":color==1?"red":color==2?"#0b1":color==3?"#f90":color)+'">['+t+']'+s+'</div>';
};
function ToA(url,txt){
	return '<a href="'+url+'" target="_blank">'+(txt||url)+'</a>';
};

function Load(url,True,False,sync){
	var xhr=new XMLHttpRequest();
	oldOpen.call(xhr,"GET", url, !sync);
	if(!sync)xhr.timeout=30000;
	var end=0;
	xhr.onreadystatechange=function(){
		if(!end && xhr.readyState==4){
			end=1;
			if(xhr.status<200 || xhr.status>299){
				var msg="请求失败["+xhr.status+"]";
				console.error(msg+" "+url,xhr);
				False&&False(msg);
			}else{
				True&&True(xhr.responseText);
			}
		}
	};
	try{
		xhr.send();
	}catch(e){
		xhr.onreadystatechange();
	}
};

Step_LoadVers();
})();
</script>

</body>
</html>